﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;

namespace Koolwired.Imap.ForDummies
{
    /// <summary>
    /// A simplified version of a mailbox class, encapsulating ImapMailbox and 
    /// providing an easy to use straightforward interface.
    /// </summary>
    public class Mailbox
    {
        bool loggedIn = false;
        int page = 0;
        int messagesPerPage = 0;
        string currentFolder = "INBOX";
        internal ImapConnect imapConnect;
        internal ImapAuthenticate imapAuthenticate;
        internal ImapCommand imapCommand;
        internal ImapMailbox imapMailbox;

        /// <summary>
        /// Gets and sets the current folder the mailbox object will be using ("INBOX" by default)
        /// </summary>
        public string CurrentFolder
        {
            get { return currentFolder; }
            set
            {
                if (!value.Equals(currentFolder))
                {
                    ClearMessages();
                    SelectCurrentFolder(value);
                }
            }
        }

        /// <summary>
        /// 0-based property that sets the current page of fetched messages, related to the PageCOunt property (for instance: Page 2 and PageCount 25 fetches messages 51-75)
        /// </summary>
        public int Page
        {
            get { return page; }
            set
            {
                if (!value.Equals(page))
                {
                    ClearMessages();
                }
                page = value;
            }
        }

        /// <summary>
        /// Property that sets the amount of messages fetched per page, related to the Page property (for instance: Page 2 and PageCount 25 fetches messages 51-75)
        /// Value of less than of equal to 0 means all messages are fetched in one page.
        /// </summary>
        public int MessagesPerPage
        {
            get { return messagesPerPage; }
            set
            {
                if (value > 0)
                {
                    if (!value.Equals(messagesPerPage))
                    {
                        ClearMessages();
                    }
                    messagesPerPage = value;
                }
            }
        }

        /// <summary>
        /// Returns the number of messages found in the imap inbox
        /// without actually fetching the message(header)s.
        /// Same can be accomplished using Mailbox.Messages.Count but 
        /// that is more expensive.
        /// </summary>
        public int MessageCount
        {
            get { return imapMailbox != null?imapMailbox.Exist:0; }
        }

        /// <summary>
        /// Returns the number of messages found in the imap inbox
        /// without actually fetching the message(header)s.
        /// Same can be accomplished using Mailbox.Messages.Count but 
        /// that is more expensive.
        /// </summary>
        public int PageCount
        {
            get { 
                int result = imapMailbox != null ? (int)Math.Ceiling((decimal)imapMailbox.Exist / messagesPerPage) : 0;
                return result > 0 ? result : 1;
            }
        }

        /// <summary>
        /// Returns a generic list of messages
        /// </summary>
        public List<MailboxMessage> Messages
        {
            get
            {
                if (imapMailbox != null)
                {
                    if (imapMailbox.Messages == null)
                    {
                        imapCommand.Fetch(imapMailbox, 1 + (page * messagesPerPage), messagesPerPage > 0 ? ((page + 1) * messagesPerPage) : MessageCount);
                    }
                    return (from m in imapMailbox.Messages select new MailboxMessage(this, m)).ToList();
                }
                else return null;
            }
        }

        public string[] AllFolders
        {
            get
            {
                return imapCommand.List("");
            }
        }

        public string[] SubFolders
        {
            get
            {
                return imapCommand.List(currentFolder);
            }
        }

        /// <summary>
        /// Clears the messages, so the next time the Messages property is accessed a fresh set will be retrieved
        /// </summary>
        internal void ClearMessages()
        {
            if (imapMailbox!= null && imapMailbox.Messages != null)
            {
                imapMailbox.Messages.Clear();
                imapMailbox.Messages = null;
                page = 0;
            }
        }

        /// <summary>
        /// Constructor to instantiate class, authenticate, connect and open the inbox all in one method
        /// </summary>
        /// <param name="host">Mailserver host name of ip address</param>
        /// <param name="username">User account name for mailbox authentication</param>
        /// <param name="password">Password of the user account for mailbox authentication</param>
        public Mailbox(string host, string username, string password)
        {
            OpenCurrentFolder(host, username, password);
        }

        /// <summary>
        /// Method to authenticate, connect and open the inbox all in one method
        /// </summary>
        /// <param name="host">Mailserver host name of ip address</param>
        /// <param name="username">User account name for mailbox authentication</param>
        /// <param name="password">Password of the user account for mailbox authentication</param>
        private void OpenCurrentFolder(string host, string username, string password)
        {
            Open(host, username, password);
            SelectCurrentFolder();
        }

        /// <summary>
        /// Selects the CurrentFolder and retrieves messagecount etc.
        /// </summary>
        private void SelectCurrentFolder()
        {
            imapMailbox = imapCommand.Select(currentFolder);
        }

        /// <summary>
        /// Sets and selects the CurrentFolder and retrieves messagecount etc.
        /// </summary>
        /// <param name="currentFolder">The folder to select</param>
        private void SelectCurrentFolder(string currentFolder)
        {
            if (!this.currentFolder.Equals(currentFolder))
            {
                this.currentFolder = currentFolder;
                SelectCurrentFolder();
            }
        }

        /// <summary>
        /// Method to authenticate and connect all in one method
        /// </summary>
        /// <param name="host">Mailserver host name of ip address</param>
        /// <param name="username">User account name for mailbox authentication</param>
        /// <param name="password">Password of the user account for mailbox authentication</param>
        private void Open(string host, string username, string password)
        {
            imapConnect = new ImapConnect(host);
            imapCommand = new ImapCommand(imapConnect);
            imapAuthenticate = new ImapAuthenticate(imapConnect, username.Replace("\\", "/"), password);
            if (imapConnect.Open())
            {
                imapAuthenticate.Login();
                loggedIn = true;
            }
        }

        /// <summary>
        /// Log out and disconnect from the mailserver
        /// </summary>
        public void Close()
        {
            if (loggedIn)
            {
                imapAuthenticate.Logout();
                imapConnect.Close();
            }
        }
    }
}
